
package org.spp.voronoi;

import java.util.ArrayList;
import java.util.List;
import org.apache.commons.math.util.MathUtils;

/**
 * Cell.java
 * @author Ilio Catallo, Eleonora Ciceri
 * @version 1.0
 * 
 * Description: the class implements a 3D cell for the VoronoiDiagram
 * 
 * Date: Jul 22, 2011
 */
public class Cell3D extends Cell {
    
   /**
     * Constructor for the class Cell3D
     * @param centroid
     * @param voronoiDiagram 
     */    
    public Cell3D(Centroid centroid, VoronoiDiagram voronoiDiagram) {
        super(centroid, voronoiDiagram);
    }
    
    /**
     * Constructor for the cell3D
     * @param centroid
     * @param id
     * @param voronoiDiagram 
     */    
    public Cell3D(Centroid centroid, int id, VoronoiDiagram voronoiDiagram)  {
        super(centroid, id, voronoiDiagram);
    }
    
    /**
     * Method for the creation of a new facet inside the cell
     * @param firstCentroid
     * @param secondCentroid
     * @return newFacet
     */
    @Override
    protected Facet createNewFacet(Centroid firstCentroid, Centroid secondCentroid) {
        Facet newPlane = new Plane(firstCentroid, secondCentroid);
        return newPlane;
    }   
    
    /**
     * Method that performs the vertex enumeration starting from the facets of the cell
     * @param A
     * @param b
     * @return vertexes
     */
    @Override
    protected List<Vertex> vertexEnumeration(Jama.Matrix A, Jama.Matrix b){
        // Variables
        List<Vertex> newVertexes = new ArrayList<Vertex>();
        
        // For each triplet of planes it computes the related vertex
        for(int i = 0; i < facets.size() - 2; i++)
            for(int j = i + 1; j < facets.size() - 1; j++)
                for(int k = j + 1; k < facets.size(); k++){                    
                    int[] rows = {i, j, k};
                    
                    Jama.Matrix subA = A.getMatrix(rows, 0, 2);
                    Jama.Matrix subb = b.getMatrix(rows, 0, 0);
                    
                    if (subA.det() == 0)
                        continue;
                    
                    // Solve the linear system
                    Jama.Matrix vertex = subA.solve(subb);
                    Vertex newVertex = new Vertex3D(vertex.getArray());

                    //Check if the vertex just computed actually belongs to the cell
                    if (!isOutside(A, b, vertex)) {
                        Vertex oldVertex = isVertexInAdjCells(newVertex);

                        // Check if already exists the same vertex in an adjacent cell
                        if (oldVertex == null){
                            newVertex.addCell(this);
                            newVertexes.add(newVertex);
                        } 
                        else 
                            oldVertex.addCell(this);
                    }                                        
                }
        
        return newVertexes;        
    }    
    
    /**
     * After the vertex enumeration we can check which facets of the bounded
     * region are shared with the cell, and remove the others from the list
     * of the cell facets
     */
    @Override
    protected void removeBoundedRegionFacets(){
        // Variables
        Double minX = Double.POSITIVE_INFINITY;
        Double minY = Double.POSITIVE_INFINITY;
        Double minZ = Double.POSITIVE_INFINITY;
        
        Double maxX = Double.NEGATIVE_INFINITY;
        Double maxY = Double.NEGATIVE_INFINITY;
        Double maxZ = Double.NEGATIVE_INFINITY;
                
        for(int i = 0; i < vertexes.size(); i++){            
            // Find the min value for each coordinate
            if (vertexes.get(i).getCoordinates()[0] < minX)
                minX = vertexes.get(i).getCoordinates()[0];
            
            if (vertexes.get(i).getCoordinates()[1] < minY)
                minY = vertexes.get(i).getCoordinates()[1];
              
            if (vertexes.get(i).getCoordinates()[2] < minZ)
                minZ = vertexes.get(i).getCoordinates()[2];
            
            // Find the max value for each coordinate
            if (vertexes.get(i).getCoordinates()[0] > maxX)
                maxX = vertexes.get(i).getCoordinates()[0];
            
            if (vertexes.get(i).getCoordinates()[1] > maxY)
                maxY = vertexes.get(i).getCoordinates()[1];
              
            if (vertexes.get(i).getCoordinates()[2] > maxZ)
                maxZ = vertexes.get(i).getCoordinates()[2];            
        }
        
        // Check if some facets of the bounded region have to be removed
        if (Math.abs(minZ + (Double)0.5) > MathUtils.EPSILON){
            facets.get(5).removeCell(this); 
            facets.remove(5);
        }
        
        if (Math.abs(maxZ - (Double)0.5) > MathUtils.EPSILON){
            facets.get(4).removeCell(this); 
            facets.remove(4);
        }
        
        if (Math.abs(minY + (Double)0.5) > MathUtils.EPSILON){
            facets.get(3).removeCell(this);
            facets.remove(3);
        }
        
        if (Math.abs(maxY - (Double)0.5) > MathUtils.EPSILON){
            facets.get(2).removeCell(this);
            facets.remove(2);
        }
        
        if (Math.abs(minX + (Double)0.5) > MathUtils.EPSILON){
            facets.get(1).removeCell(this);
            facets.remove(1);    
        }
        
        if (Math.abs(maxX - (Double)0.5) > MathUtils.EPSILON){
            facets.get(0).removeCell(this);
            facets.remove(0);
        }
    }    
}
